#本文件定义server消息的相关处理类，包括消息类型定义，封装和解封装等
#基本数据结构直接采用struct组件，其他例如list，diction数据类型采用json,
#struct为bytes类型，而json dumps后是str类型需要注意编解码转化
from common.base import base
import struct,json
from common.error import PackError

class msg(base):

    #定义异常子类
    class msgError(Exception):
        def __init__(self,info='',value=0):
            self.info = info
            self.value = value
            pass


    def __init__(self):
        super(msg,self).__init__()
        print("msg init")
        pass

    #定义消息类型为公有属性方便使用
    MSG_D_DIALOG_EXCH    = 1  # 会话交换消息sayswho+saysto+sayslen+says
    MSG_D_DIALOG         = 2  # 会话消息 sayswho+sayslen+says
    MSG_D_DIALOG_GROUP   = 3  #组播会话消息server->client，sayswho+saystogroup+sayslen+says

    __MSG_BROUD_CON = 0X00010000
    MSG_C_ALLOC_INFO     = __MSG_BROUD_CON + 1  # 用户上线，系统生成信息分配给用户
    MSG_C_CURRENT_UPLIST = __MSG_BROUD_CON + 2  # 用户上线，下发用户当前系统在线用户列表
    MSG_C_NOTIFY_NEW_UP  = __MSG_BROUD_CON + 3  # 通知用户，新用户上线
    MSG_C_AUTH_REQ       = __MSG_BROUD_CON + 4  # 认证请求消息
    MSG_C_AUTH_ACK       = __MSG_BROUD_CON + 5  # 认证应答消息，认证通过
    MSG_C_AUTH_REJ       = __MSG_BROUD_CON + 6  # 认证应答消息，认证拒绝
    MSG_C_UPLIST_REQ    =  __MSG_BROUD_CON + 7  # UPLIST请求


    #定义tl len供内部使用
    __TL_LEN = struct.calcsize('!ii')
    __I_LEN = struct.calcsize('!i')
    __II_LEN = struct.calcsize('!ii')
    __III_LEN = struct.calcsize('!iii')
    __edtype = 'utf-8' #字符到byte(s)编解码类型

    #定义错误码
    MSGERR_PACKLEN_LESS_TL = 1
    MSGERR_LENINPACK_NOT_SATISFIED = 2


    #定义封包方法，输入t,v,t为int，v_bytes为bytes，，返回网络序p_bytes,其中l自动计算添加
    def msgpack(self,t,v_bytes):
        tl_bytes=struct.pack('!ii',t,self.__TL_LEN+len(v_bytes))
        return bytes().join([tl_bytes,v_bytes])

    def msgunpack(self,pack_bytes):
        pl = len(pack_bytes)
        if pl < self.__TL_LEN:
            raise(self.msgError('pack len is not satisfied( < T_len + L_len).',self.MSGERR_PACKLEN_LESS_TL)) #pack_bytes长度不达标
        t,l = struct.unpack('!ii',pack_bytes[0:self.__TL_LEN])
        if  pl<l:
            raise (self.msgError('pack len is not satisfied( < len in pack).',self.MSGERR_LENINPACK_NOT_SATISFIED)) #包中的len和实际计算值不符
        return t,pack_bytes[self.__TL_LEN:l],pack_bytes[l:]

    def msgpack_c_alloc_info(self,ikey):
        #打包分配用户上线信息内容
        #ikey为系统自动生成和递增，在打包为i时可能会越界
        v_bytes = struct.pack('!i',ikey)
        return self.msgpack(self.MSG_C_ALLOC_INFO,v_bytes)

    def msgpack_d_dialog_exch(self,sayswho,saysto,says):#sayslen 自动计算
        says_bytes = bytes(says,encoding=self.__edtype)
        says_bylen = len(says_bytes)
        v_bytes = struct.pack('!iii%ds'%says_bylen,sayswho,saysto,says_bylen,says_bytes)
        return self.msgpack(self.MSG_D_DIALOG_EXCH,v_bytes)

    def msgpack_d_dialog(self,sayswho,says_bytes):#sayslen 自动计算
        sayswho_bytes = struct.pack('!i',sayswho)
        v_bytes = bytes().join([sayswho_bytes,says_bytes])
        return self.msgpack(self.MSG_D_DIALOG,v_bytes)

    def msgunpack_c_alloc_info(self,v_bytes):
        ikey, = struct.unpack('!i',v_bytes[:self.__I_LEN])
        return ikey,

    def msgpack_c_current_uplist(self,uplist):
        #打包当前上线用户uplist
        #print("jason dumps uplist:",json.dumps(uplist))
        v_bytes = json.dumps(uplist).encode(self.__edtype)
        return self.msgpack(self.MSG_C_CURRENT_UPLIST,v_bytes)

    def msgunpack_c_current_uplist(self,v_bytes):
        uplist = json.loads(v_bytes.decode(self.__edtype))
        return uplist

    def msgpack_c_notify_new_up(self, nlist):#nlist:[id,name]
        v_bytes = json.dumps(nlist).encode(self.__edtype)
        return self.msgpack(self.MSG_C_NOTIFY_NEW_UP, v_bytes)

    def msgunpack_c_notify_new_up(self,v_bytes):
        nlist = json.loads(v_bytes.decode(self.__edtype))
        return nlist

    # 这里特别注意，解析时says用于转发，直接返回saysbylen+says为says_bytes,使用需要注意
    def msgunpack_d_dialog_exch(self,v_bytes):
        sayswho,saysto = struct.unpack('!ii',v_bytes[:self.__II_LEN])
        return sayswho,saysto,v_bytes[self.__II_LEN:]

    def msgunpack_d_dialog(self,v_bytes):
        sayswho,saysbylen = struct.unpack('!ii',v_bytes[:self.__II_LEN])
        says = str(v_bytes[self.__II_LEN:],encoding=self.__edtype)
        return sayswho,says

    #组会话组包/解包
    def msgpack_d_dialog_group(self,sayswho,saystogroup,says_bytes):
        #says_bytes = bytes(says, encoding=self.__edtype)
        #says_bylen = len(says_bytes)
        #for server exeh  直接链接
        v_bytes = bytes().join([struct.pack('!ii',sayswho,saystogroup),says_bytes])
        #v_bytes = struct.pack('!iii%ds' % says_bylen, sayswho, saystogroup, says_bylen, says_bytes)
        return self.msgpack(self.MSG_D_DIALOG_GROUP, v_bytes)

    def msgunpack_d_dialog_group(self,v_bytes):
        sayswho, saystogroup,saysbylen = struct.unpack('!iii', v_bytes[:self.__III_LEN])
        says = str(v_bytes[self.__III_LEN:], encoding=self.__edtype)
        return sayswho, saystogroup,says

    #认证消息
    #AUTH_REQ
    def msgpack_c_auth_req(self,id,password):
        id_bytes = struct.pack('!i', id)
        password_bytes = bytes(password,encoding='utf-8')
        v_bytes = bytes().join([id_bytes,password_bytes])
        return self.msgpack(self.MSG_C_AUTH_REQ,v_bytes)

    def msgunpack_c_auth_req(self,v_bytes):
        id, = struct.unpack('!i',v_bytes[:self.__I_LEN])
        password = str(v_bytes[self.__I_LEN:],encoding='utf-8')
        return id,password

    #AUTH_ACK
    def msgpack_c_auth_ack(self,name):
        name_bytes = bytes(name,encoding='utf-8')
        v_bytes = name_bytes
        return self.msgpack(self.MSG_C_AUTH_ACK,v_bytes)

    def msgunpack_c_auth_ack(self,v_bytes):
        name = str(v_bytes,encoding='utf-8')
        return name,

    #AUTH_REJ
    def msgpack_c_auth_rej(self,reason):
        v_bytes = struct.pack('!i',reason)
        return self.msgpack(self.MSG_C_AUTH_REJ,v_bytes)
    def msgunpack_c_auth_rej(self,v_bytes):
        reason, = struct.unpack('!i',v_bytes[:self.__I_LEN])
        return reason,

    #uplistreq
    def msgunpack_c_uplist_req(self,v_bytes):
        id, = struct.unpack('!i',v_bytes[:self.__I_LEN])
        return id,

#class Ft_msg_hello
import base
class  Ft_message(base.base):
    def __init__(self,):
        super(Ft_message,self).__init__()
        print("Ft_message init")
        pass
    # 定义tl len供内部使用
    #__TL_LEN = struct.calcsize('!ii')
    __I_LEN = struct.calcsize('!i')
    __II_LEN = struct.calcsize('!ii')
    __III_LEN = struct.calcsize('!iii')
    __edtype = 'utf-8'  # 字符到byte(s)编解码类型


    # 定义消息类型为公有属性方便使用
    '''
    MSG_D_DIALOG_EXCH     = 1  # 会话交换消息sayswho+saysto+sayslen+says
    MSG_D_DIALOG          = 2  # 会话消息 sayswho+sayslen+says
    MSG_D_DIALOG_GROUP    = 3  # 组播会话消息server->client，sayswho+saystogroup+sayslen+says
    '''

    MSGD_DIALOG           = 4  #重构数据消息
    MSGD_SC_LEAVESAYS     = 5  #留言消息

    __MSG_BROUD_CON      = 0X00010000
    '''
    MSG_C_ALLOC_INFO     = __MSG_BROUD_CON + 1  # 用户上线，系统生成信息分配给用户
    MSG_C_CURRENT_UPLIST = __MSG_BROUD_CON + 2  # 用户上线，下发用户当前系统在线用户列表
    MSG_C_NOTIFY_NEW_UP  = __MSG_BROUD_CON + 3  # 通知用户，新用户上线
    MSG_C_AUTH_REQ       = __MSG_BROUD_CON + 4  # 认证请求消息
    MSG_C_AUTH_ACK       = __MSG_BROUD_CON + 5  # 认证应答消息，认证通过
    MSG_C_AUTH_REJ       = __MSG_BROUD_CON + 6  # 认证应答消息，认证拒绝
    MSG_C_UPLIST_REQ     = __MSG_BROUD_CON + 7  # UPLIST请求
    '''

    #控制消息重构
    MSGC_CS_HELLO           = __MSG_BROUD_CON + 8
    MSGC_CS_AUTH_REQ        = __MSG_BROUD_CON + 9
    MSGC_SC_AUTH_ACK        = __MSG_BROUD_CON + 10
    MSGC_SC_AUTH_REJ        = __MSG_BROUD_CON + 11
    MSGC_SC_RF_UPDATE       = __MSG_BROUD_CON + 12  #通知亲友信息

    # 定义封包方法，输入t,v,t为int，v_bytes为bytes，，返回网络序p_bytes,格式：t+v
    def __pack(self, t, v_bytes):
        t_bytes = struct.pack('!i', t)
        return bytes().join([t_bytes, v_bytes])

    def unpack(self, pack_bytes):  # t+value
        # pack_bytes长度合法性判断暂时不进行，由打包双方代码保证
        # len = len(pack_bytes)
        t, = struct.unpack('!i', pack_bytes[:self.__I_LEN])
        return t, pack_bytes[self.__I_LEN:]

    #***************************这里是server*****************************
    def unpack_hello(self,v_bytes):
        id, = struct.unpack('!i',v_bytes[:self.__I_LEN])
        return id
    def unpack_auth_req(self,v_bytes):
        id, = struct.unpack('!i', v_bytes[:self.__I_LEN])
        password = str(v_bytes[self.__I_LEN:], encoding='utf-8')
        return id, password

    def unpack_dialog(self,v_bytes):
        sayswho,saysto,issaysinfamily = struct.unpack('!iiB',v_bytes[:self.__II_LEN + struct.calcsize('B')])
        offset = self.__II_LEN + struct.calcsize('B')
        if issaysinfamily == 1:
            familyid,saystime = struct.unpack('!iq',v_bytes[offset:offset+self.__I_LEN+8])
            offset +=self.__I_LEN+8
            says = str(v_bytes[offset:],encoding=self.__edtype)
            return sayswho,saysto,True,familyid,saystime,says
        else:
            saystime, = struct.unpack('!q',v_bytes[offset:offset+8])
            offset +=8
            says = str(v_bytes[offset:], encoding=self.__edtype)
            return sayswho,saysto,False, None, saystime, says

    def pack_auth_ack(self,name):
        name_bytes = bytes(name, encoding=self.__edtype)
        v_bytes = name_bytes
        return self.__pack(self.MSGC_SC_AUTH_ACK,v_bytes)

    def pack_auth_rej(self,reason):
        v_bytes = struct.pack('!i', reason)
        return self.__pack(self.MSGC_SC_AUTH_REJ,v_bytes)

    def pack_dialog(self,sayswho,saysto,issaysinfamily,familyid,saystime,says):
        says_bytes = bytes(says,encoding=self.__edtype)
        if issaysinfamily is True:
            v_bytes = bytes().join([struct.pack('!iiBiq',sayswho,saysto,1,familyid,saystime),says_bytes])
        else:
            v_bytes = bytes().join([struct.pack('!iiBq',sayswho,saysto,0,saystime),says_bytes])
        return self.__pack(self.MSGD_DIALOG,v_bytes)

    def pack_rf_update(self,rfdict):
        v_bytes = json.dumps(rfdict).encode(self.__edtype)
        return self.__pack(self.MSGC_SC_RF_UPDATE, v_bytes)

    def pack_leavesays(self,leavesayslist):
        v_bytes = json.dumps(leavesayslist).encode(self.__edtype)
        return  self.__pack(self.MSGD_SC_LEAVESAYS, v_bytes)


    #********************************这里是client**********************

    def pack_auth_req(self,id,password):
        id_bytes = struct.pack('!i', id)
        password_bytes = bytes(password,encoding='utf-8')
        v_bytes = bytes().join([id_bytes,password_bytes])
        return self.__pack(self.MSGC_CS_AUTH_REQ,v_bytes)

    def unpack_auth_ack(self, v_bytes):
        name = str(v_bytes, encoding='utf-8')
        return name

    def unpack_auth_rej(self,v_bytes):
        reason, = struct.unpack('!i',v_bytes[:self.__I_LEN])
        return reason

    def unpack_rf_update(self,v_bytes):
        rfdict = json.loads(v_bytes.decode(self.__edtype))
        return rfdict

    def pack_hello(self,id):
        id_bytes = struct.pack('!i',self.__I_LEN)
        v_bytes = id_bytes
        return self.__pack(self.MSGC_CS_HELLO, v_bytes)

    def unpack_leavesays(self,v_bytes):
        leavesayslist = json.loads(v_bytes.decode(self.__edtype))
        return leavesayslist


#unit test
if __name__ == '__main__':
    mo = msg()
    value_bytes = struct.pack('!i',1)
    pack_bytes = mo.msgpack(msg.MSG_C_ALLOC_INFO,value_bytes)
    print(pack_bytes)
    type,len,value_bytes = mo.msgunpack(pack_bytes)
    print(type,len,value_bytes)